﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Seagull.Configuration;
using Seagull.Events;
using Seagull.Events.Drive;
using Seagull.Model.Alerts;

namespace Seagull.DAL.DriveManagers
{
    public class RemovableDriveManager : DriveManagerBase, IDisposable
    {
        private const int CheckRemovableDriveInterval = 1000;

        #region Singleton

        private static readonly Lazy<RemovableDriveManager> _instance =
            new Lazy<RemovableDriveManager>(() => new RemovableDriveManager());

        public static RemovableDriveManager Instance
        {
            get { return _instance.Value; }
        }

        #endregion

        private readonly Timer _timer;
        private readonly List<DriveInfo> _removableDrives;
        private readonly object _syncRoot = new object();

        /// <summary>
        /// 固定的可移动磁盘（用于测试）
        /// </summary>
        public DriveInfo FixedRemovableDrive;

        private RemovableDriveManager()
            :base(Config.Instance.DiskSpaceMonitorConfig.RemovableDiskFreeSpaceQuotaGB)
        {
            _removableDrives = new List<DriveInfo>();
            _timer = new Timer(CheckDrives);
        }

        #region Determine Removable Drive

        /// <summary>
        /// 取得当前的可移动磁盘
        /// </summary>
        public override DriveInfo Drive
        {
            get
            {
                if (FixedRemovableDrive != null)
                {
                    return FixedRemovableDrive;
                }
                lock (_syncRoot)
                {
                    return _removableDrives.FirstOrDefault();
                }
            }
        }

        /// <summary>
        /// 定时刷新当前的可移动磁盘盘符
        /// </summary>
        /// <param name="state"></param>
        private void CheckDrives(object state)
        {
            if (Monitor.TryEnter(_syncRoot))
            {
                try
                {
                    var removableDrives = DriveInfo.GetDrives()
                        .Where(
                            drive =>
                                drive.IsReady && !Config.Instance.PathConfig.LocalDriveLetters.Contains(drive.Name[0]))
                        .ToArray();

                    var drivesArrived =
                        removableDrives.Where(drive => _removableDrives.All(t => t.Name != drive.Name)).OrderBy(drive => drive.Name).ToArray();
                    var drivesRemoved =
                        _removableDrives.Where(drive => removableDrives.All(t => t.Name != drive.Name)).ToArray();
                    if (drivesRemoved.Any())
                    {
                        _removableDrives.RemoveAt(0);
                        DriveRemovedEvent.Instance.Publish(drivesRemoved.First());
                    }
                    // 如果之前到达的可移动盘符没有移除，则不会发布新盘符到达事件
                    if (drivesArrived.Any() && !_removableDrives.Any())
                    {
                        _removableDrives.Add(drivesArrived.First());
                        DriveArrivedEvent.Instance.Publish(drivesArrived.First());
                    }
                }
                finally
                {
                    Monitor.Exit(_syncRoot);
                }
            }
        }

        #endregion

        protected override void OnDiskFull(long freeSpaceMB)
        {
            base.OnDiskFull(freeSpaceMB);
            var drive = Drive;
            AlertEvent.Instance.Publish(
                new RemovableDiskFullAlert(drive == null ? "未找到" : drive.Name, (int)freeSpaceMB));
        }

        protected override void OnDriveSpaceChanged(int usedPercentage)
        {
            base.OnDriveSpaceChanged(usedPercentage);
            RemovableDriveSpaceChangedEvent.Instance.Publish(usedPercentage);
        }

        protected override void OnGetUsedPercentageError(IOException exception)
        {
            base.OnGetUsedPercentageError(exception);
            if (!(exception is DriveNotFoundException))
            {
                AlertEvent.Instance.Publish(new ApplicationAlert(exception, "检查移动硬盘使用量"));
            }
        }

        public override void Start()
        {
            base.Start();
            _timer.Change(5000, CheckRemovableDriveInterval);
        }

        public override void Stop()
        {
            _timer.Change(-1, -1);
            base.Stop();
        }

        public void Dispose()
        {
            if(_timer != null)
            {
                _timer.Dispose();
            }
        }
    }
}
